package at.gv.bmwf.fons.model.ontology;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Map;

import org.mindswap.pellet.jena.PelletReasonerFactory;

import at.gv.bmwf.fons.model.ontology.sources.DataSource;

import com.google.common.collect.Maps;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.ontology.OntModelSpec;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.reasoner.Reasoner;
import com.hp.hpl.jena.vocabulary.ReasonerVocabulary;

public class ModelBuilder {
	private boolean schemaBound = false;
	
	private OntModel tbox;
	private OntModel abox;
	private OntModel inferredModel;
	
	private final Map<DataSource, OntModel> additionalABoxes = Maps.newHashMap();
	private final Map<DataSource, OntModel> additionalTBoxes = Maps.newHashMap();
	
	private ModelBuilder () {
	}
	
	private void setTBox (OntModel model) {
		this.tbox = model;
	}
	
	private void setABox (OntModel model) {
		this.abox = model;
	}
		
	public static ModelBuilder load (File file, boolean asSingleSchema) throws FileNotFoundException {
		ModelBuilder builder = new ModelBuilder();
	
		builder.setTBox(ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM));
		builder.setABox(ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM));
		builder.getTBox().read(new FileInputStream(file), null, "TTL");
				
		if (asSingleSchema) {
			builder.initializeInferredModel();
		}
		
		return builder;
	}
	
	public static ModelBuilder load (File file) throws FileNotFoundException {
		return load(file, false);
	}
	
	private OntModel fromFile (File file) throws FileNotFoundException {
		OntModel model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM);
		model.read(new FileInputStream(file), null, "TTL");
		return model;
	}
	
	public void add (File file) throws FileNotFoundException {
		abox.addSubModel(fromFile(file));
	}
	
	public void addSchema (File file) throws FileNotFoundException {
		tbox.addSubModel(fromFile(file));
	}
	
	private void initializeInferredModel() {
		if (schemaBound) {
			System.err.println("Schema already bound. Rebinding with an additional T-Box is not efficient.");
		}
		System.out.println("Creating inferred model...");
		inferredModel = ModelFactory.createOntologyModel(PelletReasonerFactory.THE_SPEC);
		
		System.out.println("Binding schema...");
		Reasoner reasoner = inferredModel.getReasoner();
		reasoner.bindSchema(getTBox());

		System.out.println("Adding schema to model...");
		inferredModel.addSubModel(tbox);
		
		System.out.println("Populating with instances...");
		inferredModel.add(abox);
		
		System.out.println("Done.");
		
		schemaBound = true;
	}
	
	public void addDataSource (DataSource source) {
		source.useTBox(tbox);
		source.useABox(abox);
		source.useInferredModel(inferredModel);
		
		OntModel newTBox = source.createTBox();
		if (newTBox != null) {
			addTBox(newTBox);
			additionalTBoxes.put(source, newTBox);
		}
		
		OntModel newABox = source.createABox();
		
		if (newABox != null) {
			additionalABoxes.put(source, newABox);
			abox.addSubModel(newABox);
		}
	}
	
	private void addTBox(OntModel newTBox) {
		tbox.add(newTBox);
	}

	public OntModel getTBox () {
		return tbox;
	}
		
	public OntModel getABox () {
		return abox;
	}
	
	public OntModel getInferredModel () {
		if (!schemaBound) {
			initializeInferredModel();
		}
		return inferredModel;
	}
	
	public OntModel getModelFor (DataSource source) {
		return additionalABoxes.get(source);
	}
	
	public OntModel getSchemaFor (DataSource source) {
		return additionalTBoxes.get(source);
	}
}
